/*
 * test_func.cpp
 * Copyright (C) 2016  <@BLUEYI-PC>
 *
 * Distributed under terms of the MIT license.
 */


#include <iostream>

typedef int (*Tarr)[6];

Tarr func(int (*arr)[6])
{
    return arr;
}

int main()
{
    //**********一维数组与指针*********
    int ar[20] = {1,2,3};  //前3个元素分别是1，2，3
    int *p1 = ar; //将p1指向ar数组的第一个元素的地址，指向的是数组的元素，p1+1的增量为1个元素所占的字节空间
    int (*p2)[20] = &ar; //将p2指向ar数组的地址，即指向整个数组，p2+1的增量为&ar所代表的数组所占的字节空间
    
    //p1是一个指向int型变量的指针，p1存储的地址与ar是一样的，都是数组第一个元素的地址，
    //但p1是指针变量，ar是个常量地址，p1++等价于p1=p1+1，
    //但不能使用ar++。下标的操作在C++中会转化为对地址的操作，如ar[i]会被转化为*(ar + i)
    std::cout << "p1+1: " << p1+1 << "\tar+1: " << ar+1 
              << "\t&ar[1]: " << &ar[1] << std::endl;
    std::cout << "*(p1+1): " << *(p1+1) << "\t*(ar+1): " << *(ar+1) << std::endl;
    //p2是一个指针，不同于int *p2[20];不加小括号时，p2会优先与后面的[20]结合，导致p2成为指针数组
    //即含有20个int型指针的数组。而加了小括号p2先与*结合，p2成为指针，该指针指向一个含有20个int
    //型元素的数组，恰好与ar数组类型相同。注意ar是一个数组，数组名表示的是该数组的第一个元素的地址
    //例外情况是对数组名使用sizeof运算符将获得整个数组的大小（单位是字节）
    //而对数组名取地址时，得到的将是该连续20个地址空间的首地址，虽然值相同，但意义不同
    //&ar[0]/p1/ar表示的是一个4字节的内存块的首地址，而&ar却表示20*4个字节的内存块首地址  
    //相当于此时对p1/ar加1时增加的最小单位是4个字节的连续内存空间，也就是对应第二个元素，而对&ar/p2加1
    //是毫无意义的，增加是20*4个字节的连续内存空间，将会越界，
    //可对其解引用一次得到数组第一个元素的地址，即*p2等价于p1
    std::cout << "p2: " << p2 << "\t&ar: " << &ar << "\tar: " << ar << "\t&ar[0]: " << &ar[0] << std::endl;
    std::cout << "*p2: " << *p2 << "\tp1: " << p1 << "\t**p2: " << **p2 << "\t*p1: " << *p1 << std::endl;
    
    //**********二维数组与指针*********
    std::cout << "**********二维数组与指针*********" << std::endl;
    //首先C/C++中没有内置的二维数组，多维数组是都是指数组的数组，使用时很多情况下可以视为二维数组
    int arr[5][6] = {{1,2,3}}; //5行6列的数组，实际是一个含有5个元素的一维数组，每个元素又是一个含有6个int值的数组
    int (*pp1)[6] = arr;  //pp1是一个指向含有6个int元素的数组的指针
    int (*pp2)[5][6] = &arr;  //pp2是一个指向含有5行6列的二维数组的地址，即指向的是整个数组的数组
    
    //二维数组arr[i][j]会被解析成*(*(arr + i) + j)，arr依然是第一个元素的地址，
    //此处的第一个元素即可以表示arr[0][0]（即二维数组的第一个元素），也可以表示以行为元素的一维数组的
    //第一个元素*(arr + 0)（即第一行的6个元素），显然这两个地址是相同的。所以对arr增加1，即移到下一个
    //元素的地址，下一个元素即第二行。arr的元素是含有6个int的一维数组，所以指向arr的指针类型也必须是含有
    //6个int的一维数组，所以*pp1的括号和后面的[6]必须要有。
    //pp2的解释与p2类似
    
    std::cout << "&arr[0][0]: " << &arr[0][0] << "\tarr: " << &arr << "\tpp1: " << pp1
              << "\t*pp2: " << *pp2 << "\t**arr: " << **arr << std::endl;

    //使用指针访问二维数组
    for (int (*pr)[6] = arr; pr != arr + 5; ++pr) {
        for (int *pc = *pr; pc != *pr + 6; ++pc)
            std::cout << *pc << " ";
        std::cout << std::endl;
    }

    //**********函数和数组*********
    //首先明确C/C++中函数无法返回数组，所以对数组的操作和返回只能通过指针，而数组名本身就是个地址
    //可以看成是个常量指针
    
    int c[5][6];
    Tarr b = func(c);
    std::cout << "&b[1][1]: " << &b[1][1] << "\t&c[1][1]: " << &c[1][1] << std::endl;
    return 0;
}

